/**
 * @file simpleBLEPeripheral.c
 * @author chipsea
 * @brief 
 * @version 0.1
 * @date 2020-11-30
 * @copyright Copyright (c) 2020, CHIPSEA Co., Ltd.
 * @note
 */
#include "sdk_config.h"
#include "ll.h"
#include "hci.h"
#include "OSAL.h"
#include "gatt.h"
#include "gpio.h"
#include "pwrmgr.h"
#include "ll_def.h"
#include "hci_tl.h"
#include "bcomdef.h"
#include "ll_hw_drv.h"
#include "gapbondmgr.h"
#include "peripheral.h"
#include "OSAL_PwrMgr.h"
#include "gattservapp.h"
#include "gapgattserver.h"
#include "rf_phy_driver.h"
#include "global_config.h"
//#include "devinfoservice.h"
#include "simpleBLEPeripheral.h"

///< How often to perform periodic event
#define SBP_PERIODIC_EVT_PERIOD             5000

#define DEVINFO_SYSTEM_ID_LEN				8
#define DEVINFO_SYSTEM_ID					0
 
#define DEFAULT_DISCOVERABLE_MODE			GAP_ADTYPE_FLAGS_GENERAL

///< Default passcode
#define DEFAULT_PASSCODE					0

///< Length of bd addr as a string
#define B_ADDR_STR_LEN						15

#define RESOLVING_LIST_ENTRY_NUM			10


///< build define
#define APP_CFG_RPA_TEST					0


/*********************************************************************
 * GLOBAL VARIABLES
 */
uint8_t connect_status = false;
uint16_t gapConnHandle;
perStatsByChan_t g_perStatsByChanTest;

/*********************************************************************
 * EXTERNAL VARIABLES
 */
volatile uint8_t g_current_advType = LL_ADV_CONNECTABLE_UNDIRECTED_EVT;

//extern wtnrTest_t wtnrTest;
extern l2capSARDbugCnt_t g_sarDbgCnt;
extern uint32 g_osal_mem_allo_cnt;
extern uint32 g_osal_mem_free_cnt;

extern uint32 counter_tracking;

extern uint32 g_counter_traking_avg;
extern uint32 g_counter_traking_cnt;
extern uint32_t  g_TIM2_IRQ_TIM3_CurrCount;
extern uint32_t  g_TIM2_IRQ_to_Sleep_DeltTick;
extern uint32_t  g_osal_tick_trim;
extern uint32_t  g_TIM2_IRQ_PendingTick;
extern uint32_t  g_TIM2_wakeup_delay;

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */


/*********************************************************************
 * LOCAL VARIABLES
 */
static uint8 simpleBLEPeripheral_TaskID;   ///< Task ID for internal task/event processing

static gaprole_States_t gapProfileState = GAPROLE_INIT;


#if(APP_CFG_RPA_TEST==1)
static uint8  peerIrkList[RESOLVING_LIST_ENTRY_NUM][LL_ENC_IRK_LEN];  
static uint8  localIrkList[RESOLVING_LIST_ENTRY_NUM][LL_ENC_IRK_LEN];  
static uint8  peerAddrList[RESOLVING_LIST_ENTRY_NUM][LL_DEVICE_ADDR_LEN];  
static uint8  peerAddrType[RESOLVING_LIST_ENTRY_NUM];  
#endif


///< GAP - SCAN RSP data (max size = 31 bytes)
const uint8 scanRspData[9] =
{
    ///< connection interval range
    0x05,	///< length of this data
    GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE,
    LO_UINT16( DEFAULT_DESIRED_MIN_CONN_INTERVAL ),	///< 100ms
    HI_UINT16( DEFAULT_DESIRED_MIN_CONN_INTERVAL ),
    LO_UINT16( DEFAULT_DESIRED_MAX_CONN_INTERVAL ),	///< 1s
    HI_UINT16( DEFAULT_DESIRED_MAX_CONN_INTERVAL ),

    ///< Tx power level
    0x02,	///< length of this data
    GAP_ADTYPE_POWER_LEVEL,
    0		///< 0dBm
};


///< advert data for iBeacon
const uint8 advertData[7] =
{	
	0x02,	///< length of this data
	GAP_ADTYPE_FLAGS,
	DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,

//	0x03,	///< length of this data including the data type byte
//	GAP_ADTYPE_16BIT_MORE, ///< manufacturer specific adv data type
//	LO_UINT16( BLE_UART_SERV_UUID ),
//	HI_UINT16( BLE_UART_SERV_UUID )
};

///< GAP GATT Attributes
uint8 attDeviceName[GAP_DEVICE_NAME_LEN] = DEVICE_NAME;

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static void simpleBLEPeripheral_ProcessOSALMsg( osal_event_hdr_t *pMsg );
static void peripheralStateNotificationCB( gaprole_States_t newState );

static void peripheralStateReadRssiCB( int8 rssi  );

#if(APP_CFG_RPA_TEST==1)
static void initResolvingList(void);
#endif

void check_PerStatsProcess(void);

char *bdAddr2Str( uint8 *pAddr );
//static uint8_t simpleBLEPeripheral_ScanRequestFilterCBack(void);
/*********************************************************************
 * PROFILE CALLBACKS
 */

///< GAP Role Callbacks
static gapRolesCBs_t simpleBLEPeripheral_PeripheralCBs =
{
    peripheralStateNotificationCB,	///< Profile State Change Callbacks
    peripheralStateReadRssiCB		///< When a valid RSSI is read from controller (not used by application)
};
#if (DEFAULT_GAPBOND_MGR_ENABLE==1)
//GAP Bond Manager Callbacks, add 2017-11-15
static gapBondCBs_t simpleBLEPeripheral_BondMgrCBs =
{
  NULL,		///< Passcode callback (not used by application)
  NULL		///< Pairing / Bonding state Callback (not used by application)
};
#endif

/*********************************************************************
 * PUBLIC FUNCTIONS
 */
/**
 * @fn		void SimpleBLEPeripheral_Init( uint8 task_id )
 * @brief	Initialization function for the Simple BLE Peripheral App Task.
 *          This is called during initialization and should contain
 *          any application specific initialization (ie. hardware
 *          initialization/setup, table initialization, power up
 *          notificaiton ... ).
 * @param	task_id - the ID assigned by OSAL.  This ID should be
 *                    used to send messages and set timers.
 * @return	none
 */
void SimpleBLEPeripheral_Init( uint8 task_id )
{
	simpleBLEPeripheral_TaskID = task_id;
    
	///< Setup the GAP
	VOID GAP_SetParamValue( TGAP_CONN_PAUSE_PERIPHERAL, DEFAULT_CONN_PAUSE_PERIPHERAL );

	///< Setup the GAP Peripheral Role Profile
	{
		///< device starts advertising upon initialization
		uint8 initial_advertising_enable = true;

		uint8 enable_update_request = DEFAULT_ENABLE_UPDATE_REQUEST;
		uint8 advChnMap = GAP_ADVCHAN_37 | GAP_ADVCHAN_38 | GAP_ADVCHAN_39; 
		
		///< By setting this to zero, the device will go into the waiting state after
		///< being discoverable for 30.72 second, and will not being advertising again
		///< until the enabler is set back to TRUE
		uint16 gapRole_AdvertOffTime = 0;

		uint16 desired_min_interval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
		uint16 desired_max_interval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;
		uint16 desired_slave_latency = DEFAULT_DESIRED_SLAVE_LATENCY;
		uint16 desired_conn_timeout = DEFAULT_DESIRED_CONN_TIMEOUT;
		
		uint8 peerPublicAddr[] = {
			0x01,
			0x02,
			0x03,
			0x04,
			0x05,
			0x06
		};

		uint8 advType = g_current_advType;	///< it seems a  bug to set GAP_ADTYPE_ADV_NONCONN_IND = 0x03
		GAPRole_SetParameter( GAPROLE_ADV_EVENT_TYPE, sizeof( uint8 ), &advType );
		
		GAPRole_SetParameter(GAPROLE_ADV_DIRECT_ADDR, sizeof(peerPublicAddr), peerPublicAddr);
		///< set adv channel map
		GAPRole_SetParameter(GAPROLE_ADV_CHANNEL_MAP, sizeof(uint8), &advChnMap);        

		///< Set the GAP Role Parameters
		GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &initial_advertising_enable );
		GAPRole_SetParameter( GAPROLE_ADVERT_OFF_TIME, sizeof( uint16 ), &gapRole_AdvertOffTime );
		
		uint8  advData[B_MAX_ADV_LEN];
		uint8 offset;
		//set scan_rsp information
		offset = 0;
        osal_memset(attDeviceName, 0, sizeof(attDeviceName));
		GAPRole_EncodeAdvDeviceName(advData, &offset, B_MAX_ADV_LEN, attDeviceName, osal_strlen((char *)attDeviceName));
		osal_memcpy(advData+offset, scanRspData, sizeof(scanRspData));
		GAPRole_SetParameter( GAPROLE_SCAN_RSP_DATA, offset + sizeof ( scanRspData ), advData );
		
		//set adv_data information
		osal_memcpy(advData, advertData, sizeof(advertData));
		offset = sizeof(advertData);
		GAPRole_EncodeAdvDeviceAddr(advData, &offset, B_MAX_ADV_LEN);
		GAPRole_SetParameter( GAPROLE_ADVERT_DATA, offset, advData );

		GAPRole_SetParameter( GAPROLE_PARAM_UPDATE_ENABLE, sizeof( uint8 ), &enable_update_request );
		GAPRole_SetParameter( GAPROLE_MIN_CONN_INTERVAL, sizeof( uint16 ), &desired_min_interval );
		GAPRole_SetParameter( GAPROLE_MAX_CONN_INTERVAL, sizeof( uint16 ), &desired_max_interval );
		GAPRole_SetParameter( GAPROLE_SLAVE_LATENCY, sizeof( uint16 ), &desired_slave_latency );
		GAPRole_SetParameter( GAPROLE_TIMEOUT_MULTIPLIER, sizeof( uint16 ), &desired_conn_timeout );
	}

	///< Set the GAP Characteristics
	GGS_SetParameter( GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName );

    ///< Set advertising interval
    {
        uint16 advInt = DEFAULT_DESIRED_ADV_INTERVAL;	///< actual time = advInt * 625us

        GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MIN, advInt );
        GAP_SetParamValue( TGAP_LIM_DISC_ADV_INT_MAX, advInt );
        GAP_SetParamValue( TGAP_GEN_DISC_ADV_INT_MIN, advInt );
        GAP_SetParamValue( TGAP_GEN_DISC_ADV_INT_MAX, advInt );
    }
#if(DEFAULT_GAPBOND_MGR_ENABLE==1)
    // Setup the GAP Bond Manager, add 2017-11-15
    {
        uint32 passkey = DEFAULT_PASSCODE;
        uint8 pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
        uint8 mitm = TRUE;
        uint8 ioCap = GAPBOND_IO_CAP_NO_INPUT_NO_OUTPUT;
        uint8 bonding = TRUE;
        GAPBondMgr_SetParameter( GAPBOND_DEFAULT_PASSCODE, sizeof ( uint32 ), &passkey );
        GAPBondMgr_SetParameter( GAPBOND_PAIRING_MODE, sizeof ( uint8 ), &pairMode );
        GAPBondMgr_SetParameter( GAPBOND_MITM_PROTECTION, sizeof ( uint8 ), &mitm );
        GAPBondMgr_SetParameter( GAPBOND_IO_CAPABILITIES, sizeof ( uint8 ), &ioCap );
        GAPBondMgr_SetParameter( GAPBOND_BONDING_ENABLED, sizeof ( uint8 ), &bonding );
    }
#endif
    ///< Initialize GATT attributes
    GGS_AddService( GATT_ALL_SERVICES );			///< GAP
    GATTServApp_AddService( GATT_ALL_SERVICES );	///< GATT attributes

    #if (1)
    {
        uint8_t mtuSet = 247;
        llInitFeatureSet2MPHY(TRUE);
        llInitFeatureSetDLE(TRUE);
        ATT_SetMTUSizeMax(mtuSet);
        LOG("[2Mbps | DLE | MTU %d] \n", mtuSet);
    }
    #else
		ATT_SetMTUSizeMax(23);
		llInitFeatureSet2MPHY(FALSE);
		llInitFeatureSetDLE(FALSE);
    #endif
    ///< Setup a delayed profile startup
    osal_set_event( simpleBLEPeripheral_TaskID, SBP_START_DEVICE_EVT );
    ///< for receive HCI complete message
    GAP_RegisterForHCIMsgs(simpleBLEPeripheral_TaskID);

    #if(APP_CFG_RPA_TEST)
	///< ========================= For Resolving Private Address testing
	uint8 addrType = ADDRTYPE_PUBLIC;

	initResolvingList();
	GAPRole_SetParameter(GAPROLE_ADV_DIRECT_TYPE, 1, &addrType);
	GAPRole_SetParameter(GAPROLE_ADV_DIRECT_ADDR, LL_DEVICE_ADDR_LEN, peerAddrList[0]);
	LOG("======== RPA Init ========\n");
	#endif

    LL_PerStats_Init(&g_perStatsByChanTest);
    LOG("======================SimpleBLEPeripheral_Init Done====================\n");
}

/**
 * @fn		uint16 SimpleBLEPeripheral_ProcessEvent( uint8 task_id, uint16 events )
 * @brief	Simple BLE Peripheral Application Task event processor.  This function
 *          is called to process all events for the task.  Events
 *          include timers, messages and any other user defined events.
 * @param	task_id - The OSAL assigned task ID.
 * @param   events  - events to process.  This is a bit map and can
 *                    contain more than one event.
 * @return	events not processed
 */
uint16 SimpleBLEPeripheral_ProcessEvent( uint8 task_id, uint16 events )
{
    VOID task_id;	///< OSAL required parameter that isn't used in this function

    if ( events & SYS_EVENT_MSG )
    {
        uint8 *pMsg;

        if ( (pMsg = osal_msg_receive( simpleBLEPeripheral_TaskID )) != NULL )
        {
            simpleBLEPeripheral_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );

            ///< Release the OSAL message
            VOID osal_msg_deallocate( pMsg );
        }

        ///< return unprocessed events
        return (events ^ SYS_EVENT_MSG);
    }

    if ( events & SBP_START_DEVICE_EVT )
    {
        ///< Start the Device
        VOID GAPRole_StartDevice( &simpleBLEPeripheral_PeripheralCBs );
    #if(DEFAULT_GAPBOND_MGR_ENABLE==1)
        // Start Bond Manager, 2017-11-15
		LOG("bond\r\n");
        VOID GAPBondMgr_Register( &simpleBLEPeripheral_BondMgrCBs );
    #endif
        // Set timer for first periodic event
        //osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_PERIODIC_EVT, SBP_PERIODIC_EVT_PERIOD );
        
        HCI_LE_ReadResolvingListSizeCmd();

        return ( events ^ SBP_START_DEVICE_EVT );
    }
	
	///< enable adv
    if ( events & SBP_RESET_ADV_EVT )
    {
        uint8 initial_advertising_enable = TRUE;
		
        GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &initial_advertising_enable );	
	  
        return ( events ^ SBP_RESET_ADV_EVT );
    }
	
    ///< Discard unknown events
    return 0;
}

/**
 * @fn		void simpleBLEPeripheral_ProcessOSALMsg( osal_event_hdr_t *pMsg )
 * @brief	Process an incoming task message.
 * @param	pMsg - message to process
 * @return	none
 */
static void simpleBLEPeripheral_ProcessOSALMsg( osal_event_hdr_t *pMsg )
{
	hciEvt_CmdComplete_t *pHciMsg;

	switch ( pMsg->event )
	{
		case HCI_GAP_EVENT_EVENT:
		{
			switch( pMsg->status )
			{
				case HCI_COMMAND_COMPLETE_EVENT_CODE:
					pHciMsg = (hciEvt_CmdComplete_t *)pMsg;

					LOG("==> HCI_COMMAND_COMPLETE_EVENT_CODE: %x\n", pHciMsg->cmdOpcode);
					//safeToDealloc = gapProcessHCICmdCompleteEvt( (hciEvt_CmdComplete_t *)pMsg );
				break;

				default:
					//safeToDealloc = FALSE;  ///< Send to app
				break;
			}
		}
	}
}

/**
 * @fn		void peripheralStateReadRssiCB( int8  rssi )
 * @brief	Notification from the profile of a state change.
 * @param	rssi
 * @return	none
 */
static void peripheralStateReadRssiCB( int8  rssi )
{
	
}

/**
 * @fn		void peripheralStateNotificationCB( gaprole_States_t newState )
 * @brief	Notification from the profile of a state change.
 * @param	newState - new state
 * @return	none
 */
static void peripheralStateNotificationCB( gaprole_States_t newState )
{
    switch ( newState )
    {
        case GAPROLE_STARTED:
        {
            uint8 ownAddress[B_ADDR_LEN];
//			uint8 initial_advertising_enable = false;
//        
            GAPRole_GetParameter(GAPROLE_BD_ADDR, ownAddress);
            #if(0) 
            uint8 systemId[DEVINFO_SYSTEM_ID_LEN];
            ///< use 6 bytes of device address for 8 bytes of system ID value
            systemId[0] = ownAddress[0];
            systemId[1] = ownAddress[1];
            systemId[2] = ownAddress[2];
        
            ///< set middle bytes to zero
            systemId[4] = 0x00;
            systemId[3] = 0x00;
        
            ///< shift three bytes up
            systemId[7] = ownAddress[5];
            systemId[6] = ownAddress[4];
            systemId[5] = ownAddress[3];
        
            DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);
            #endif    

            #if(APP_CFG_RPA_TEST)
            ///< init RPA list and enable resolving address
            for (int i = 0; i < 3; i ++)
                HCI_LE_AddDevToResolvingListCmd(peerAddrType[i], peerAddrList[i], peerIrkList[i], localIrkList[i]);

			HCI_LE_SetResolvablePrivateAddressTimeoutCmd(60);

			HCI_LE_SetAddressResolutionEnableCmd(TRUE);
            #endif
			LOG("MAC address:%x:%x:%x:%x:%x:%x\n",ownAddress[5],ownAddress[4],ownAddress[3],ownAddress[2],ownAddress[1],ownAddress[0]);
//			GAPRole_SetParameter( GAPROLE_ADVERT_ENABLED, sizeof( uint8 ), &initial_advertising_enable );
//			osal_set_event(simpleBLEPeripheral_TaskID, SBP_RESET_ADV_EVT);  
        }
        break;
        
        case GAPROLE_ADVERTISING:
        break;
        
        case GAPROLE_CONNECTED:
			GAPRole_GetParameter( GAPROLE_CONNHANDLE, &gapConnHandle );
            HCI_ConnEventDoneNoticeCmd(simpleBLEPeripheral_TaskID, NULL);
        break;
        
        case GAPROLE_CONNECTED_ADV:
			
        break;
		
        case GAPROLE_WAITING:
            
        break;
        
        case GAPROLE_WAITING_AFTER_TIMEOUT:
            
        break;
        
        case GAPROLE_ERROR:
			
        break;
        
        default:
			
        break;
    }
    gapProfileState = newState;

    LOG("[GAP ROLE %d]\n",newState);
     
    VOID gapProfileState;     
}

/**
 * @fn		char *bdAddr2Str( uint8 *pAddr )
 * @brief	Convert Bluetooth address to string. Only needed when
 *			LCD display is used.
 * @param	pAddr - Bluetooth address
 * @return	char - string
 */
char *bdAddr2Str( uint8 *pAddr )
{
  uint8       i;
  char        hex[] = "0123456789ABCDEF";
  static char str[B_ADDR_STR_LEN];
  char        *pStr = str;
  
  *pStr++ = '0';
  *pStr++ = 'x';
  
  // Start from end of addr
  pAddr += B_ADDR_LEN;
  
  for ( i = B_ADDR_LEN; i > 0; i-- )
  {
    *pStr++ = hex[*--pAddr >> 4];
    *pStr++ = hex[*pAddr & 0x0F];
  }
  
  *pStr = 0;
  
  return str;
}

#if(APP_CFG_RPA_TEST==1)
/**
 * @fn		void initResolvingList(void)
 * @brief	
 * @param	none
 * @return	none
 */
static void initResolvingList(void)
{
    int i;
	uint8 temp;

	for (i = 0; i < RESOLVING_LIST_ENTRY_NUM; i++)
	{
	    osal_memset(&peerIrkList[i], (i + 1), LL_ENC_IRK_LEN);
		osal_memset(&localIrkList[i], (i + 1), LL_ENC_IRK_LEN);
		
        temp = ((i + 1) << 4) | (i + 1);
		osal_memset(&peerAddrList[i], temp, LL_DEVICE_ADDR_LEN);
		peerAddrType[i] = LL_DEV_ADDR_TYPE_PUBLIC;      // LL_DEV_ADDR_TYPE_RANDOM
	}
}
#endif

/**
 * @fn		void check_PerStatsProcess(void)
 * @brief	
 * @param	none
 * @return	none
 */
void check_PerStatsProcess(void)
{
    perStats_t perStats;
    uint16_t perRxNumTotal=0;
    uint16_t perRxCrcErrTotal=0;
    uint16_t perTxNumTotal=0;
    uint16_t perTxAckTotal=0;
    uint16_t perRxToCntTotal=0;
    uint16_t perConnEvtTotal=0;

    LOG("[PER STATS Notify]\r");
    LOG("----- ch connN rxNum rxCrc rxToN txAck txRty \r");
    for(uint8 i=0;i<37;i++)
    {
        LL_PerStasReadByChn(i,&perStats);
        LOG("[PER] %02d %05d %05d %05d %05d %05d %05d\n",i,perStats.connEvtCnt,
                                                     perStats.rxNumPkts,
                                                     perStats.rxNumCrcErr,
                                                     perStats.rxToCnt,
                                                     perStats.TxNumAck,
                                                     perStats.txNumRetry);
        
        perConnEvtTotal+= perStats.connEvtCnt;
        perRxNumTotal+= perStats.rxNumPkts;
        perRxCrcErrTotal+= perStats.rxNumCrcErr;
        perRxToCntTotal+= perStats.rxToCnt;
        perTxAckTotal+= perStats.TxNumAck;
        perTxNumTotal+= perStats.txNumRetry;
                        
    }
    LOG("TOTAL ch connN rxNum rxCrc rxToN txAck txRty \r");
    LOG("\n[PER] -- %05d %05d %05d %05d %05d %05d\n",perConnEvtTotal,
                                                     perRxNumTotal,
                                                     perRxCrcErrTotal,
                                                     perRxToCntTotal,
                                                     perTxAckTotal,
                                                     perTxNumTotal);
    LL_PerStatsReset();

}

/*********************************************************************
*********************************************************************/
